package dispatcher.person.gui.network;

import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;

import dispatcher.gui.event.PersonStatusInitializeThread;
import dispatcher.gui.event.PersonStatusUpdateThreadPool;

import messages.BaseMessage;
import messages.DeregistrationMessage;
import messages.MessageFactory;
import messages.MessageType;
import messages.persondispatcher.CurrentPeopleStateMessage;
import messages.persondispatcher.CurrentPeopleStateRequest;
import messages.persondispatcher.PersonCreationMessage;

import observer.ObserverThread;
import state.Globals;

/**
 * This class is handles all connections to the person dispatcher. Once connected to 
 * the person dispatcher it will then query the whole person dispatcher state. Once the
 * state has been retrieved, it will then wait for any new event to arrive. This thread is
 * able to send person creation message to the person dispatcher to be enterred into the
 * system
 * @author Caleb
 *
 */
public class PersonDispatcherGUIObserverThread extends ObserverThread{

	public PersonDispatcherGUIObserverThread(InetAddress remoteAddr, int remotePort) {
		super(remoteAddr, remotePort);
	}
	
	/**
	 * This method will attempt to reconnect to the PersonDispatcher server. It will attempt to reconnect the socket
	 * and will then reinitialise all the people data
	 * @deprecated Does not restart the thread
	 */
	public void reconnectToServer(){
		// Initialise the socket
		if (!init()){
            if (getRemoteSocket() != null){
                try{
                	getRemoteSocket().close();
                }catch (IOException e){
                    Globals.getLogger().error("Could not close socket after failed reconnect", e);
                }
            }
            Globals.getLogger().log("Could not initiate a connection, shutting down thread", this.getClass().getName(), "reconnectToServer");
            return;
        }
		// Call the pre-run conditions
		if (!onStart())
        {
        	try
        	{
        		getRemoteSocket().close();
        	}
        	catch(IOException e)
        	{
        	    Globals.getLogger().error("Could not close socket after failed onStart", e);
        	}
        	Globals.getLogger().log("onStart failed, shutting down thread", this.getClass().getName(), "run");
        }
	}
	
	/**
	 * This method will run the pre-run conditions needed before the thread starts running
	 */
	@Override
	public boolean onStart(){
		// Request the current state of the PersonDispatcher
		CurrentPeopleStateRequest message = new CurrentPeopleStateRequest();
		try{
			getRemoteSocket().getOutputStream().write(message.toByteArray());
		}catch(IOException e){
			Globals.getLogger().error("Could not send CurrentPeopleStateRequest message", e);
        	Globals.getLogger().log("Could not send CurrentPeopleStateRequest message, shutting down thread", this.getClass().getName(), "onStart");
			return false;
		}
		// Waits for the return message
		BaseMessage returnMess = null;
		
		try{
			returnMess = MessageFactory.readMessage(getRemoteSocket());
		}catch(IOException e){
			Globals.getLogger().error("Could not read CurrentPeopleStateMessage message", e);
        	Globals.getLogger().log("Could not read CurrentPeopleStateMessage message, shutting down thread", this.getClass().getName(), "onStart");
			return false;
		}
		// Checks if the return message is of the right type
		if (returnMess.getMessageType() == MessageType.CurrentPeopleStateMessage){
			CurrentPeopleStateMessage mess = (CurrentPeopleStateMessage)returnMess;
			try{
				// Creates a request to initialise the PersonDispatcherStateHandler
				(new PersonStatusInitializeThread(mess.getPersonDispatcherState())).start();
			}catch (IOException e){
				Globals.getLogger().error("Could not parse PersonDispatcherState", e);
	        	Globals.getLogger().log("Could not parse PersonDispatcherState, shutting down thread", this.getClass().getName(), "onStart");
				return false;
			}
			return true;
		}
		
		try {
			this.getRemoteSocket().setTcpNoDelay(true);
		} catch (SocketException e) {
			e.printStackTrace();
		}
		
    	Globals.getLogger().log("Message was not CurrentPeopleStateMessage, was : " + returnMess.getMessageType(), this.getClass().getName(), "onStart");
		return false;
	}
	
	/**
	 * This method will send the create person message to the person dispatcher
	 * @param message The create person message
	 * @return Return true if message was send successfully
	 */
	public synchronized boolean sendPersonMessage(PersonCreationMessage message){
		try{
			getRemoteSocket().getOutputStream().write(message.toByteArray());
			getRemoteSocket().getOutputStream().flush();
			// Temp fix to stop multiple messages in same package
			Thread.sleep(100);
		}catch(IOException e){
			Globals.getLogger().error("Could not send PersonCreationMessage message", e);
        	Globals.getLogger().log("Could not send PersonCreationMessage message, shutting down thread", this.getClass().getName(), "sendPersonMessage");
        	return false;
		}catch(NullPointerException e){
			Globals.getLogger().error("Could not open port", e);
        	Globals.getLogger().log("Could not open port, shutting down thread", this.getClass().getName(), "sendPersonMessage");
        	return false;
		} catch (InterruptedException e) {
			Globals.getLogger().error("Could not sleep", e);
        	Globals.getLogger().log("Could not sleep, shutting down thread", this.getClass().getName(), "sendPersonMessage");
        	return false;
		}
		Globals.getLogger().log("Send Person Successful", this.getClass().getName(), "sendPersonMessage");
		return true;
	}
	
	public synchronized boolean sendTerminationMessage(){
		DeregistrationMessage message = new DeregistrationMessage();
		try{
			getRemoteSocket().getOutputStream().write(message.toByteArray());
		}catch(IOException e){
			Globals.getLogger().error("Could not send BaseMessage message", e);
        	Globals.getLogger().log("Could not send BaseMessage message, shutting down thread", this.getClass().getName(), "sendTerminationMessage");
        	return false;
		}catch(NullPointerException e){
			Globals.getLogger().error("Could not open port", e);
        	Globals.getLogger().log("Could not open port, shutting down thread", this.getClass().getName(), "sendTerminationMessage");
        	return false;
		}
		Globals.getLogger().log("Deregister message sent", this.getClass().getName(), "sendTerminationMessage");
		PersonStatusUpdateThreadPool.getInstance().terminate();
		return true;
	}
}
